package cn.wd.tx;

import cn.hutool.core.util.StrUtil;
import cn.wd.domain.LoginLog;
import cn.wd.service.ILoginLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.net.UnknownHostException;

@Aspect
@Component
public class MyTransactionManager {

    private static final String AOP_POINTCUT_SERVICE_IMPL = "execution(* cn.wd.service.impl.*ServiceImpl.login(..))";
    private static final String LOGIN_SUCCESS = "登录成功";
    private static final String LOGIN_FAIL    = "登录失败";
    private static final String STRING_PREFIX = "I";
    private static final String STRING_SUFFIX = "Service";

    private final ILoginLogService loginLogService;
    public MyTransactionManager(ILoginLogService loginLogService) {
        this.loginLogService = loginLogService;
    }

    // 定义切入点 WHERE
    @Pointcut(AOP_POINTCUT_SERVICE_IMPL)
    public void txPointcut() {}

    //异常通知：在方法出现异常时进行通知,可以访问到异常对象，且可以指定在出现特定异常时在执行通知。
    @AfterThrowing(value = "txPointcut()", throwing = "ex")
    public void throwingLog(JoinPoint joinPoint, Exception ex) throws UnknownHostException {
        // 1. 获取异常信息
        String exceptionMsg = ex.getMessage();
        // 2. 创建日志对象
        LoginLog loginLog = MyTransactionManager.newLoginLog(joinPoint, MyTransactionManager.LOGIN_FAIL, exceptionMsg);
        // 3. 把日志对象存入数据库
        loginLogService.save(loginLog);
    }

    //后置通知：在方法正常执行完成进行通知，可以访问到方法的返回值的。
    @AfterReturning(value = "txPointcut()", returning = "returning")
    public void successLog(JoinPoint joinPoint, String returning) throws UnknownHostException {
        // 1. 把获取到的返回值类型进行强转
        // 2. 创建日志对象
        LoginLog loginLog = MyTransactionManager.newLoginLog(joinPoint, MyTransactionManager.LOGIN_SUCCESS, returning);
        // 3. 把日志对象存入数据库
        loginLogService.save(loginLog);
    }

    private static LoginLog newLoginLog(JoinPoint joinPoint, String info, String msg) throws UnknownHostException {
        /* 1. 获取需要增强的方法的 所属类 和 参数列表 以及 异常信息 */
        // 1. 获取用户当前登录的用户类型
        String className = joinPoint.getSignature().getDeclaringType().getSimpleName();
        className = StrUtil.unWrap(className, MyTransactionManager.STRING_PREFIX, MyTransactionManager.STRING_SUFFIX);
        String LoginUserType = MyTransactionManager.loginUserType2CN(className);

        // 2. 通过获取参数列表获取当前登录的用户名
        String username = (String)joinPoint.getArgs()[0];

        // 获取请求对象
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String remoteAddr = "访问不到该用户的 ip 地址";
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();
            // 远程客户端 ip 地址
            /*remoteAddr = request.getRemoteAddr();*/
            remoteAddr = MyTransactionManager.getIP(request);

        }

        LoginLog loginLog = new LoginLog();
        loginLog.setName(username);
        loginLog.setIp(remoteAddr);
        loginLog.setLoginUserType(LoginUserType);
        loginLog.setInfo(info);
        loginLog.setContent(msg);
        return loginLog;
    }

    private static String getIP(HttpServletRequest request)  {
        if  (request.getHeader("x-forwarded-for")  ==  null)  {
            return  request.getRemoteAddr();
        }
        return  request.getHeader("x-forwarded-for");
    }

    private static String loginUserType2CN(String className){
        switch (className){
            case "User": return "用户";
            case "Employee": return "员工";
            default: return "其他";
        }
    }
}
